package sd.settemmezzo.player.thread;

import java.rmi.RemoteException;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.log4j.Logger;

import sd.settemmezzo.common.Player;
import sd.settemmezzo.crash.CrashManager;
import sd.settemmezzo.graphic.UI_Client;
import sd.settemmezzo.player.PlayerImplement;
import sd.settemmezzo.player.state.GlobalState;
import sd.settemmezzo.player.state.LocalState;
import sd.settemmezzo.util.Message;

public class SendThreadFactory {

	private SendThread thread;
	private PingThread ping;
	private Timer timer;
	
	private static Logger log = Logger.getLogger("SendThreadFactory");
	final boolean DEBUG = false;

	public class SendThread extends Thread {
		
		private GlobalState gstate;
		private Message message;
		private LocalState lstate;
		private Player nextPlayer;

		public SendThread(Message message, Player nextPlayer) {
			this.message = message;
			this.lstate = LocalState.getInstance();
			this.nextPlayer = nextPlayer;
		}

		@Override
		public synchronized void run() {
			Thread.currentThread().setName("SendThread");
			try {
				/* Provo ad invocare il metodo remoto */
				nextPlayer.getPlayerInterface().send(message);
				log.info("[SendThread:run] Invio dello stato globale a " + nextPlayer.getNamePlayer() + " effettuato.");
			} catch (RemoteException e) {
				log.error("[SendThread:run] Il nodo " + nextPlayer.getId() + ":" + nextPlayer.getNamePlayer() + " è in crash" );
				
				/* Se il nodo è crashato invoco il CrashManager... */
				gstate = CrashManager.crashGlobalState(nextPlayer, (GlobalState) message.getObject());
				
				//message = new Messaggio(message.getType(), gstate);
				message = new Message(Message.CRASH_DETECTED, gstate);
				
				/* Player utilizzato per il tentativo successivo */
				nextPlayer = gstate.getRing().getNext(lstate.getId());

				/* e continuo con l'invio sul nuovo nextPlayer */
				run();
			}
		}
	}

	public class PingThread extends TimerTask {
		
		private Player nextPlayer;
		private LocalState ls;
		private GlobalState gstate;

		public PingThread(GlobalState gs) {
			ls = LocalState.getInstance();
			gstate = gs;
		}

		@Override
		public synchronized void run() {
			Thread.currentThread().setName("PingThread");
			gstate = PlayerImplement.gstate;
			nextPlayer = gstate.getRing().getNext(ls.getId());
			
			try {
				log.info("[PingThread] Are you alive " + nextPlayer.getNamePlayer() + "?");
				nextPlayer.getPlayerInterface().areYouAlive();
			} catch (RemoteException e) {
				log.error("[PingThread] Catch: " + nextPlayer.getNamePlayer() + " è morto.");
				
				if (nextPlayer.getId() != gstate.getToken()) {
				
					gstate.getRing().crash(nextPlayer);
					run();
				
				} else {
				
					gstate = CrashManager.crashOnPing(nextPlayer, gstate);
	
					gstate.notifyChanged(UI_Client.getInstance(ls.getName()));
					
					// qui manca il mazziere se è crashato lui!!
				
					Message m = new Message(Message.CRASH_DETECTED, gstate);
					startSendThread(m, gstate.getRing().getNext(ls.getId()));
					
				}
			}
		}
	}


	public void startSendThread(Message m, Player nextPlayer) {
		thread = new SendThread(m, nextPlayer);
		thread.start();
	}

	public void startPingThread(GlobalState gs) {
		ping = new PingThread(gs);
		timer = new Timer();
		log.warn("[startPingThread] PingThread creato.");
		timer.schedule(ping, 10000, 30000);
	}
}
